#include "Video.h"
#include "app_common.h"
#include "DevConfig.h"
#include "CaptureIsp.h"
#include "Color2Grey.h"
#include "OsdDraw.h"
#include "System.h"
#include "hi_mw_media_intf.h"

static pthread_mutex_t gs_VideoMutex[VIDEO_VENC_CHN_MAX_NUM]= {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};
static VideoVencDataCallback gs_pfnLiteosVencDataCb[VIDEO_VENC_CHN_MAX_NUM];

typedef struct {
    char bInited;
}HISI_LITEOS_VIDEO_CONTEXT_S;

HISI_LITEOS_VIDEO_CONTEXT_S g_hisiLiteosVideoCtx = {
    .bInited = 0,
};

HISI_LITEOS_VIDEO_CONTEXT_S *hisi_liteos_video_get_ctx(void)
{
    return &g_hisiLiteosVideoCtx;
}

void *tskOsdInit(void *args)
{
    SET_THREAD_NAME("tskOsdInit");
    pthread_detach(pthread_self());  

    int ret = 0;
    
    /* set system time from mcu */
    SYSTEM_TIME stTime;
    memset(&stTime, 0, sizeof(stTime));
    ret = SocSubGetTime(&stTime);
    if (ret >= 0) {
        SystemSetCurrentTime(&stTime);
    }

	/* set timezone */
	TIMEZONE_CONFIG stTimeZone;
	SYS_TIMEZONE_INFO_S stSysTimeZone;
	
	memset(&stTimeZone, 0, sizeof(stTimeZone));
	DevConfigGetTimeZone(&stTimeZone);
	memset(&stSysTimeZone, 0, sizeof(stSysTimeZone));
	stSysTimeZone.zoneSec = stTimeZone.zoneSec;
	stSysTimeZone.stDayLightTime.bEnable = stTimeZone.bEnable;
	stSysTimeZone.stDayLightTime.startTime = stTimeZone.startTime;
	stSysTimeZone.stDayLightTime.endTime = stTimeZone.endTime;
	ret = SystemSetTimeZone(stSysTimeZone);
	if (ret < 0) {
		LOGE("SystemSetTimeZone failed\n");
		return -1;
	}

    ret = OsdDrawInit();
    if (ret < 0) {
        LOGE("OsdDrawInit failed\n");
        return -1;
    }
    
    ret = OsdDrawStart();
    if (ret < 0) {
        LOGE("OsdDrawStart failed\n");
        return -1;
    }
    
    pthread_exit(0);
}

int hisi_liteos_video_init(void)
{
    int ret = 0;
    pthread_t osdInitPid = 0;
    HISI_LITEOS_VIDEO_CONTEXT_S *ctx = hisi_liteos_video_get_ctx();

    /* video init */
    ret = HalVideoInit();
    if (ret < 0) {
        LOGE("HalVideoInit failed\n");
        return -1;
    }

    /* color to grey init */
    ret = Color2GreyInit();
    if (ret < 0) {
        LOGE("Color2GreyInit failed\n");
        return -1;
    }
#if 0
    /* isp init */
    ret = CaptureIspInit();
    if (ret < 0) {
        LOGE("CaptureIspInit failed\n");
        return -1;
    }
#endif
    /* init osd task */
    ret = pthread_create(&osdInitPid, NULL, &tskOsdInit, NULL);
    if (ret != 0) {
        LOGE("pthread_create tskOsdInit failed!\n");
        return -1;
    } 

#ifdef QRCODE_ENABLE
    QrcodeInit();
#endif /* QRCODE_ENABLE */

    LOGI("init successed\n");
    ctx->bInited = 1;
    
    return 0;
}

int hisi_liteos_video_deinit(void)
{
    int ret = 0;  
    HISI_LITEOS_VIDEO_CONTEXT_S *ctx = hisi_liteos_video_get_ctx();

    hisi_liteos_video_venc_stop();
    ret = HalVideoDeInit();
    if (ret < 0) {
        LOGE("HalVideoDeInit failed\n");
        return -1;
    }
    ctx->bInited = 0;

 #ifdef QRCODE_ENABLE
    QrcodeDeinit();
#endif /* QRCODE_ENABLE */

    return 0;
}

int hisi_liteos_video_set_resolution_without_config(VIDEO_CHN_E chn, VIDEO_STREAM_ATTR_S stStreamAttr)
{
    int ret = 0;
    HI_MW_VEncAttr stVencAttr;

    AV_MUTEX_LOCK(gs_VideoMutex[chn]);
    memset(&stVencAttr, 0, sizeof(stVencAttr));
    stVencAttr.HANDLE = chn;
    stVencAttr.PAYLOAD_TYPE = HI_MW_PAYLOAD_TYPE_H264;
    stVencAttr.WIDTH = stStreamAttr.width;
    stVencAttr.HEIGHT =stStreamAttr.height;
    stVencAttr.BUFSIZE = stStreamAttr.width * stStreamAttr.height * 2;
    stVencAttr.PROFILE = 0;
    stVencAttr.BASE = 1;
    stVencAttr.PROFILE = 0;
    stVencAttr.ENABLEPRED = HI_TRUE;
    stVencAttr.BITRATE = stStreamAttr.bitrate;
    stVencAttr.GOP = stStreamAttr.framerate * 4;
    stVencAttr.FRAMERATE = stStreamAttr.framerate;
    
    ret = HalVideoSetResolution(chn, &stVencAttr);
    if (ret < 0) {
        LOGE("HalVideoSetResolution chn:%d failed", chn);
        AV_MUTEX_UNLOCK(gs_VideoMutex[chn]);
        return -1;
    }

    AV_MUTEX_UNLOCK(gs_VideoMutex[chn]);
    
    DAYNIGHT_MODE_E enDayNightMode = Color2GreyGetState();
    ret = Color2GreySetMode(enDayNightMode);
    if (ret < 0) {
        LOGE("Color2GreySetMode failed\n");
        return -1;
    }

//    LOGW("stVencAttr.WIDTH:%d stVencAttr.HEIGHT:%d stVencAttr.BITRATE:%d\n", stVencAttr.WIDTH, stVencAttr.HEIGHT, stVencAttr.BITRATE);
    
    return 0;

}

int hisi_liteos_video_set_resolution(VIDEO_CHN_E chn, VIDEO_STREAM_ATTR_S stStreamAttr)
{
    int ret = 0;
    ret = hisi_liteos_video_set_resolution_without_config(chn, stStreamAttr);
    if (ret < 0) {
       LOGE("hisi_liteos_video_set_resolution_without_config failed\n");
       return -1;
    }

    /* update config */
    DevConfigSetVideoResolution(stStreamAttr.width, stStreamAttr.height, stStreamAttr.bitrate);
    
    return 0;
}

int hisi_liteos_vidoe_get_resolution(VIDEO_CHN_E chn, VIDEO_STREAM_ATTR_S *pstStreamAttr)
{
    int ret = 0;
    HI_MW_VEncAttr stVencAttr;

    if (pstStreamAttr == NULL) {
        LOGE("param is invalid\n");
        return -1;
    }    

    AV_MUTEX_LOCK(gs_VideoMutex[chn]);
    memset(&stVencAttr, 0, sizeof(stVencAttr));
    ret = HalVideoGetResolution(chn, &stVencAttr);
    if (ret < 0) {
        LOGE("HalVideoGetResolution failed\n");
        AV_MUTEX_UNLOCK(gs_VideoMutex[chn]);
        return -1;    
    }

    pstStreamAttr->nGop = stVencAttr.GOP;
    pstStreamAttr->framerate = stVencAttr.FRAMERATE;
    pstStreamAttr->width = stVencAttr.WIDTH;
    pstStreamAttr->height = stVencAttr.HEIGHT;
    
    AV_MUTEX_UNLOCK(gs_VideoMutex[chn]);
    
    return 0;

}

int hisi_liteos_video_request_idr_frame(VIDEO_CHN_E chn)
{
    int ret = 0;
    int venChn = chn;

    AV_MUTEX_LOCK(gs_VideoMutex[chn]);
    ret = HalVideoVencRequestIDR(venChn);
    if (ret < 0) {
        LOGE("HalVideoVencRequestIDR venChn:%d failed\n", venChn);
        AV_MUTEX_UNLOCK(gs_VideoMutex[chn]);
        return ret;
    }

    AV_MUTEX_UNLOCK(gs_VideoMutex[chn]);
    return ret;
}


int hisi_liteos_video_set_flip(VIDEO_FLIP_ATTR_S stFlipAttr)
{
    return 0;
}

int hisi_liteos_video_get_flip(VIDEO_FLIP_ATTR_S *pstFlipAttr)
{
    return 0;
}

int hisi_liteos_video_set_daynight_mode(VIDEO_CHN_E chn, VIDEO_NIGHT_MODE_E enNightMode)
{
    int ret = 0;
    char bEnable = 0;

    switch (enNightMode)
    {
        case VIDEO_NIGHT_OFF:
            bEnable = 0;
            break;
        case VIDEO_NIGHT_ON:
            bEnable = 1;
            break;
        case VIDEO_NIGHT_AUTO:
            break;
        default:
            LOGW("params (%d) is invalid!\n", enNightMode);
            break;
    }
    
    ret = HalVideoSetColor2Grey(chn, bEnable);
    if (ret < 0) {
        LOGE("HalVideoSetColor2Grey chn:%d failed\n", chn);
        return -1;
    }
    
    return 0;
}

int hisi_liteos_video_get_daynight_mode(VIDEO_CHN_E chn, VIDEO_NIGHT_MODE_E *pNightMode)
{
    return 0;
}

int hisi_liteos_video_get_snap_pic(unsigned char **pData, int *pSize)
{
    unsigned char *pSnapBuf = NULL;
    int snapMaxSize = 50 * 1024;
    int snapSize = 0;
    int ret = 0;    
    HISI_LITEOS_VIDEO_CONTEXT_S *ctx = hisi_liteos_video_get_ctx();

    if (!ctx->bInited) {
        LOGW("video don't init\n");
        return -1;
    }

    pSnapBuf = (unsigned char *)malloc(snapMaxSize);
    if (pSnapBuf == NULL) {
        LOGE("malloc pSnapBuf size %d failed\n", snapMaxSize);
        return -1;
    }
    
    ret = HalVideoGetSnapPic(VIDEO_CHN_3TRD, pSnapBuf, snapMaxSize, &snapSize);
    if (ret < 0) {
        LOGE("HalVideoGetSnapPic failed\n");
        free(pSnapBuf);
        return -1;
    }

    *pData = pSnapBuf; /* 由调用者来释放 */
    *pSize = snapSize;
    
    return 0;
}


int OnHisiVideoMainStreamData(HI_HANDLE VencHdl, HI_MW_MEDIA_VIDEO_S* pVStreamData, HI_VOID* pPrivateData)
{
    int ret = 0;
    int chn = (int)VencHdl;

    if (NULL != gs_pfnLiteosVencDataCb[chn]) {
        ret = gs_pfnLiteosVencDataCb[chn](chn, (void *)pVStreamData);
    }

    return ret;
}

int OnHisiVideoSecondStreamData(HI_HANDLE VencHdl, HI_MW_MEDIA_VIDEO_S* pVStreamData, HI_VOID* pPrivateData)
{
    int ret = 0;
    int chn = (int)VencHdl;

    if (NULL != gs_pfnLiteosVencDataCb[chn]) {
        ret = gs_pfnLiteosVencDataCb[chn](chn, (void *)pVStreamData);
    }

    return ret;
}

int hisi_liteos_video_venc_start(int chn, VideoVencDataCallback pfnVencDataCb)
{
    int ret = 0;
    
    gs_pfnLiteosVencDataCb[chn] = pfnVencDataCb;

    switch (chn)
    {
        case 0:
            ret = HalVideoVencStart(0, OnHisiVideoMainStreamData);
            break;
        case 1:
            ret = HalVideoVencStart(1, OnHisiVideoSecondStreamData);
            break;
    }

    if (ret < 0) {
        LOGE("HalVideoVencStart chn(%d) failed\n", chn);
        return -1;
    }
   
    return 0;
}

int hisi_liteos_video_venc_stop(int chn, VideoVencDataCallback pfnVencDataCb)
{
    int ret = 0;
    
    gs_pfnLiteosVencDataCb[chn] = NULL;

    switch (chn)
    {
        case 0:
            ret = HalVideoVencStop(0, OnHisiVideoMainStreamData);
            break;
        case 1:
            ret = HalVideoVencStop(1, OnHisiVideoSecondStreamData);
            break;
    }

    if (ret < 0) {
        LOGE("HalVideoVencStop chn(%d) failed\n", chn);
        return -1;
    }
    
    return 0;
}

int hisi_liteos_video_copy_data(int chn, void *data, MEDIA_PACK_VIDEO *pstPackVideo, int maxSize)
{
    HI_MW_MEDIA_VIDEO_S *pVStreamData = (HI_MW_MEDIA_VIDEO_S *)data;
    
	int i = 0;
	int len_cnt = 0;
    HI_U8* pPackVirtAddr = NULL;
    HI_U32 u32PackLen = 0;
    HI_U32 u32PackOffset = 0;
    
    for (i = 0; i < pVStreamData->u32PackCount; i++)
    {
        /* copy video frame type */
        if (HI_MW_PAYLOAD_TYPE_H264 == pVStreamData->astPack[i].stDataType.enPayloadType)
        {
            pstPackVideo->stDataType.enEncType = MEDIA_ENC_TYPE_H264;
            if (HI_MW_H264E_NALU_ISLICE == pVStreamData->astPack[i].stDataType.enH264EType
                || HI_MW_H264E_NALU_IDRSLICE == pVStreamData->astPack[i].stDataType.enH264EType
                || HI_MW_H264E_NALU_SPS == pVStreamData->astPack[0].stDataType.enH264EType)
            {
            
                pstPackVideo->stDataType.enFrameType = MEDIA_VIDEO_FRAME_TYPE_I;
            }
            else
            {
                 pstPackVideo->stDataType.enFrameType = MEDIA_VIDEO_FRAME_TYPE_P;
            }
        }
        else if (HI_MW_PAYLOAD_TYPE_H265 == pVStreamData->astPack[i].stDataType.enPayloadType)
        {   
            pstPackVideo->stDataType.enEncType = MEDIA_ENC_TYPE_H265;
            if (HI_MW_H265E_NALU_ISLICE == pVStreamData->astPack[i].stDataType.enH265EType
                || HI_MW_H265E_NALU_IDRSLICE == pVStreamData->astPack[i].stDataType.enH265EType
                || HI_MW_H265E_NALU_SPS == pVStreamData->astPack[0].stDataType.enH265EType)
            {
                  pstPackVideo->stDataType.enFrameType = MEDIA_VIDEO_FRAME_TYPE_I;
            }
            else
            {
                pstPackVideo->stDataType.enFrameType = MEDIA_VIDEO_FRAME_TYPE_P;
            }
        }
        else
        {
            LOGW("enPayloadType is invalid!\n");
            return -1;
        }

        /* copy video frame timestamp */
        pstPackVideo->timestamp =  pVStreamData->astPack[i].u64PTS;
        
        /* copy video frame data */
        pPackVirtAddr = pVStreamData->astPack[i].pu8Addr[0];
        u32PackLen = pVStreamData->astPack[i].au32Len[0];
        u32PackOffset = pVStreamData->astPack[i].u32Offset; 
        if(len_cnt + (u32PackLen - u32PackOffset) > maxSize) { //防止当前帧大于开辟缓存大小
            return -1;
        }
        memcpy(pstPackVideo->pFrameData + len_cnt, pPackVirtAddr + u32PackOffset,  u32PackLen - u32PackOffset);
        len_cnt += u32PackLen - u32PackOffset;

        if (2 == pVStreamData->astPack[i].u32SegCnt) { //处理一个包有两段的数据
            pPackVirtAddr = pVStreamData->astPack[i].pu8Addr[1];
            u32PackLen = pVStreamData->astPack[i].au32Len[1];
            memcpy(pstPackVideo->pFrameData + len_cnt, pPackVirtAddr + u32PackOffset,  u32PackLen - u32PackOffset);
            len_cnt += u32PackLen - u32PackOffset;
        }
    }

    /* copy video frame size */
    pstPackVideo->frameSize = len_cnt;

    /* copy video frame seq */
    pstPackVideo->seq = pVStreamData->u32Seq;
    
	return 0;
}

static VIDEO_OPS_FUNC_S gs_stHisiLiteosVideoFuncOps = {
    .init                            = hisi_liteos_video_init,
    .deinit                         = hisi_liteos_video_deinit,

    .video_venc_start               = hisi_liteos_video_venc_start,
    .video_venc_stop                = hisi_liteos_video_venc_stop,

    .video_copy_data                = hisi_liteos_video_copy_data,

    .set_resolution                 = hisi_liteos_video_set_resolution,
    .set_resolution_without_config  = hisi_liteos_video_set_resolution_without_config,
    .get_resolution                 = hisi_liteos_vidoe_get_resolution,

    .request_idr_frame              = hisi_liteos_video_request_idr_frame,

    .set_flip                       = hisi_liteos_video_set_flip,
    .get_flip                       = hisi_liteos_video_get_flip,

    .set_daynight_mode              = hisi_liteos_video_set_daynight_mode,
    .get_daynight_mode              = hisi_liteos_video_get_daynight_mode,

    .get_snap_pic                   = hisi_liteos_video_get_snap_pic,
};

VIDEO_OPS_FUNC_S *hisi_liteos_video_ops_handle(void)
{
	return &gs_stHisiLiteosVideoFuncOps;
}
